package com.executor.gateway.core.util;

import com.executor.gateway.model.po.IpList;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;

/**
 * 基于Redis的响应式布隆过滤器服务
 * 用于高效的IP黑白名单检查
 * 
 * @author miaoguoxin
 * @date 2024/01/01
 */
@Service
public class BloomFilterUtils {

    @Resource(name = "bloomReactiveRedisTemplate")
    private ReactiveRedisTemplate<String, String> bloomRedisTemplate;

    private static final int DEFAULT_SIZE = 2 << 24;
    private static final int[] SEEDS = new int[]{3, 13, 46, 71, 91, 134};

    /**
     * 计算哈希值
     */
    private int hash(String value, int seed) {
        int result = 0;
        int len = value.length();
        for (int i = 0; i < len; i++) {
            result = seed * result + value.charAt(i);
        }
        return (DEFAULT_SIZE - 1) & result;
    }

    /**
     * 添加元素到布隆过滤器
     */
    public Mono<Void> add(String key, String value) {
        return Flux.range(0, SEEDS.length)
                .map(i -> hash(value, SEEDS[i]) % DEFAULT_SIZE)
                .flatMap(bitIndex -> bloomRedisTemplate.opsForValue().setBit(key, bitIndex, true))
                .then();
    }

    /**
     * 检查元素是否可能存在于布隆过滤器中
     */
    public Mono<Boolean> mightContain(String key, String value) {
        return isExists(key)
                .flatMap(exists -> {
                    if (!exists) {
                        return Mono.just(false);
                    }
                    
                    // 使用响应式方式检查所有位
                    return Flux.range(0, SEEDS.length)
                            .map(i -> hash(value, SEEDS[i]) % DEFAULT_SIZE)
                            .flatMap(bitIndex -> bloomRedisTemplate.opsForValue().getBit(key, bitIndex))
                            .all(bit -> bit != null && bit)
                            .defaultIfEmpty(false);
                });
    }

    /**
     * 检查元素是否可能存在于布隆过滤器中（支持类型参数）
     */
    public boolean mightContain(String ip, Integer type) {
        String key = getBloomFilterKey(type);
        return mightContain(key, ip).block();
    }

    /**
     * 标记布隆过滤器已存在
     */
    private Mono<Void> markAsExists(String key) {
        return bloomRedisTemplate.opsForValue().set(key + ":flag", "exists").then();
    }

    /**
     * 检查布隆过滤器是否存在
     */
    private Mono<Boolean> isExists(String key) {
        return bloomRedisTemplate.opsForValue().get(key + ":flag")
                .map(value -> value != null)
                .defaultIfEmpty(false);
    }

    /**
     * 使用MD5计算更稳定的哈希值（可选方法）
     */
    private int hashWithMD5(String value, int seed) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] digest = md.digest((value + seed).getBytes(StandardCharsets.UTF_8));
            int result = 0;
            for (int i = 0; i < 4; i++) {
                result = (result << 8) | (digest[i] & 0xFF);
            }
            return Math.abs(result) % DEFAULT_SIZE;
        } catch (NoSuchAlgorithmException e) {
            // 如果MD5不可用，回退到简单哈希
            return hash(value, seed);
        }
    }

    /**
     * 估算布隆过滤器中元素的数量（近似值）
     */
    public Mono<Long> estimateElementCount(String key) {
        return isExists(key)
                .flatMap(exists -> {
                    if (!exists) {
                        return Mono.just(0L);
                    }
                    
                    // 这里可以实现更精确的估算逻辑
                    // 简化实现，返回一个基于key的估算值
                    return Mono.just(100L); // 占位符实现
                });
    }

    /**
     * 获取布隆过滤器的误判率（理论值）
     */
    public double getFalsePositiveRate(long elementCount) {
        if (elementCount <= 0) {
            return 0.0;
        }
        
        int k = SEEDS.length; // 哈希函数个数
        int m = DEFAULT_SIZE;  // 位数组大小
        
        // 理论误判率：(1 - e^(-kn/m))^k
        double exponent = -1.0 * k * elementCount / m;
        return Math.pow(1 - Math.exp(exponent), k);
    }

    /**
     * 根据IP类型获取布隆过滤器的键
     */
    private String getBloomFilterKey(Integer type) {
        if (IpList.BLACK_TYPE == type) {
            return "bloom_filter:blacklist";
        } else if (IpList.WHITE_TYPE == type) {
            return "bloom_filter:whitelist";
        } else {
            throw new IllegalArgumentException("Unsupported IP list type: " + type);
        }
    }

    /**
     * 检查白名单是否存在
     */
    public boolean isWhitelistExists(Integer type) {
        String key = getBloomFilterKey(type);
        return isExists(key).block();
    }

    /**
     * 从数据库中加载IP列表到布隆过滤器
     * 这是核心的初始化方法
     */
    public Mono<Void> initBloomFilterWithIpListObjects(List<IpList> ipLists) {
        if (ipLists == null || ipLists.isEmpty()) {
            return Mono.empty();
        }

        // 分别处理黑名单和白名单
        return Flux.fromIterable(ipLists)
                .groupBy(IpList::getIpListType)
                .flatMap(group -> {
                    String key = getBloomFilterKey(group.key());
                    
                    // 先清空现有的布隆过滤器
                    return clearBloomFilter(key)
                            .then(group.collectList())
                            .flatMap(ipList -> {
                                if (ipList.isEmpty()) {
                                    return Mono.empty();
                                }
                                
                                // 批量添加IP到布隆过滤器
                                return Flux.fromIterable(ipList)
                                        .flatMap(ip -> addIpToBloomFilter(key, ip.getIp()))
                                        .then(markAsExists(key));
                            });
                })
                .then();
    }

    /**
     * 添加单个IP到布隆过滤器
     */
    private Mono<Void> addIpToBloomFilter(String key, String ip) {
        if (ip == null || ip.trim().isEmpty()) {
            return Mono.empty();
        }
        return Flux.range(0, SEEDS.length)
                .map(i -> hash(ip.trim(), SEEDS[i]) % DEFAULT_SIZE)
                .flatMap(bitIndex -> bloomRedisTemplate.opsForValue().setBit(key, bitIndex, true))
                .then();
    }

    /**
     * 清空布隆过滤器
     */
    private Mono<Void> clearBloomFilter(String key) {
        return bloomRedisTemplate.delete(key)
                .then(bloomRedisTemplate.delete(key + ":flag"))
                .then();
    }

    /**
     * 添加新的IP到现有的布隆过滤器（不清空现有数据）
     */
    public Mono<Void> addIpToFilter(String ip, Integer type) {
        String key = getBloomFilterKey(type);
        return addIpToBloomFilter(key, ip)
                .then(markAsExists(key));
    }

    /**
     * 重新初始化指定类型的布隆过滤器
     */
    public Mono<Void> reinitBloomFilter(List<IpList> ipLists, Integer type) {
        return Flux.fromIterable(ipLists)
                .filter(ipList -> type.equals(ipList.getIpListType()))
                .collectList()
                .flatMap(filteredList -> {
                    if (filteredList.isEmpty()) {
                        // 如果没有数据，清空布隆过滤器
                        String key = getBloomFilterKey(type);
                        return clearBloomFilter(key);
                    }
                    return initBloomFilterWithIpListObjects(filteredList);
                });
    }

    /**
     * 获取布隆过滤器统计信息
     */
    public Mono<BloomFilterStats> getBloomFilterStats(Integer type) {
        String key = getBloomFilterKey(type);
        return isExists(key)
                .flatMap(exists -> {
                    if (!exists) {
                        return Mono.just(new BloomFilterStats(type, 0L, 0.0, false));
                    }
                    
                    return estimateElementCount(key)
                            .map(count -> {
                                double falsePositiveRate = getFalsePositiveRate(count);
                                return new BloomFilterStats(type, count, falsePositiveRate, true);
                            });
                });
    }

    /**
     * 布隆过滤器统计信息
     */
    public static class BloomFilterStats {
        private final Integer type;
        private final Long elementCount;
        private final Double falsePositiveRate;
        private final Boolean exists;

        public BloomFilterStats(Integer type, Long elementCount, Double falsePositiveRate, Boolean exists) {
            this.type = type;
            this.elementCount = elementCount;
            this.falsePositiveRate = falsePositiveRate;
            this.exists = exists;
        }

        public Integer getType() { return type; }
        public Long getElementCount() { return elementCount; }
        public Double getFalsePositiveRate() { return falsePositiveRate; }
        public Boolean getExists() { return exists; }
    }
}